// import { log } from "console";

// import { log } from "console";

// const arr: Array<number | string> = [1, 2, 3, 4, 5];
// const arr2 = ['a', 'b', 'c']

// const arr3 = arr.concat(arr2)
// const arr4 = arr2.copyWithin(1, 2)

// const odd = arr.filter((el) => el as number % 2 !== 0)

// const even = arr.filter((el) => el as number % 2 === 0)

// arr.splice(0, 0, ...arr2)

// console.log(`arr is`, arr)

// console.log(`rr3 is`, arr3)

// console.log(`arr4 is `, arr4)
// log(`odd is`, odd)
// log(`even is`, even)

// function creatObjArr(): Array<{
//     id: number;
//     name: string;
// }> {
//     return [
//         {
//             id: 1,
//             name: 'a',
//         },
//         {
//             id: 2,
//             name: 'b',
//         },
//     ]
// }

// const objArr = creatObjArr()

// console.log(`reduct arr is`, objArr.reduce((acc, cur) => { return cur.id + acc }, 0))

// const buf =  new ArrayBuffer(6);
// const view = new DataView(buf);

// console.log(view.getInt32(4))

// const m = new Map([
//     ['a',1],
//     ['b',2],
//     ['c',3],
// ])

// for (const etr of m) {
//     // console.log(etr[0], etr[1])
//     console.log(etr[0], etr[1])
// }

import assert from 'node:assert';

function createArr() {
  return [1, 2, 3, 4, 5, 4, 5, 'a', 'a', 1];
}

function createArr2() {
  return [1, 2, 3, 4, [5, 4, 5, ['a', 'a', 1]]];
}

function createMap() {
  return new Map([
    ['a', 1],
    ['b', 2],
    ['c', 3],
    ['d', 4],
    ['e', 5],
    ['f', 6],
    ['g', 7],
    ['h', 8],
    ['i', 9],
    ['j', 10],
    ['k', 11],
    ['l', 12],
    ['m', 13],
    ['n', 14],
    ['o', 15],
  ]);
}
function createMap2() {
  return new Map([
    ['a', 2],
    ['b', 3],
    ['c', 4],
    ['d', 5],
    ['e', 6],
    ['f', 6],
    ['g', 7],
    ['h', 8],
    ['i', 9],
    ['j', 10],
    ['k', 11],
    ['l', 12],
    ['m', 13],
    ['n', 14],
    ['o', 15],
  ]);
}

const arr = createArr();

const arr2 = createArr2();

const map = createMap();

const map2 = createMap2();

function unique(arr: unknown[], origin = false) {
  if (origin) {
    arr.splice(0, arr.length, ...Array.from(new Set(arr)));
    return arr;
  }
  return Array.from(new Set(arr));
}

function flatten(arr: unknown[], level = 1) {
  return arr.flat(level);
}

function mapToArr(map: Map<unknown, unknown>) {
  return Array.from(map);
}

function mergeMap(map1: Map<unknown, unknown>, map2: Map<unknown, unknown>) {
  return new Map([...map1, ...map2]);
}

function findMaxValue(map: Map<unknown, number>) {
  let maxKey: unknown = null,
    maxValue: number = -Infinity;

  for (const [k, v] of map) {
    if (v > maxValue) {
      maxValue = v;
      maxKey = k;
    }
  }
  return maxKey;
}

function intersection(set: Set<unknown>, set2: Set<unknown>) {
  const [lset, sset] = set.size > set2.size ? [set, set2] : [set2, set];
  const result = new Set();
  for (const el of sset) {
    if (lset.has(el)) result.add(el);
  }

  return result;
}

function filterSet(set: Set<unknown>, callback: (el: unknown) => boolean) {
  const result = new Set();
  for (const el of set) {
    if (callback(el)) result.add(el);
  }
  return result;
}

function randomElement<T>(set: Set<T>): T | undefined {
  if (set.size === 0) return undefined;
  const rnum = Math.floor(Math.random() * set.size);
  let i = 0;
  for (const el of set) {
    if (i === rnum) return el;
    i++;
  }
  return undefined;
}

// type CacheMap = WeakMap<object, Map<string | symbol, Map<string, unknown>>>;

/**生成对象方法结果缓存器
 * 注意使用时，缓存的方法应该是单纯函数而且应该是幂等的，即同样的入参的结果永远是一样的
 */
type CacheEntry = {
  methodMap: Map<string, unknown>;
  origfn: (this: unknown, ...args: unknown[]) => unknown;
  desc?: PropertyDescriptor | undefined;
};

function createCache() {
  // 用于缓存的弱映射对象：对象 -> (method -> entry)
  const cacheMap: WeakMap<
    object,
    Map<string | symbol, CacheEntry>
  > = new WeakMap();

  return {
    /** 注册方法缓存 */
    set<T extends object, K extends keyof T>(obj: T, method: K) {
      const key = method as unknown as string | symbol;
      const origfn = obj[method] as unknown as (
        this: unknown,
        ...args: unknown[]
      ) => unknown;
      if (typeof origfn !== 'function') {
        throw new TypeError(`Property ${String(method)} is not a function`);
      }

      let map = cacheMap.get(obj);
      if (!map) {
        map = new Map<string | symbol, CacheEntry>();
        cacheMap.set(obj, map);
      }

      // 已经包装过则跳过（避免重复包裹）
      if (map.has(key)) return;

      const methodMap = new Map<string, unknown>();

      function replace(this: unknown, ...arg: unknown[]): unknown {
        const argkey = generateCacheKey(arg);
        if (methodMap.has(argkey)) {
          return methodMap.get(argkey);
        }
        const result = origfn.apply(this, arg);
        methodMap.set(argkey, result);
        return result;
      }

      // 保留原描述符并替换方法
      const desc = Object.getOwnPropertyDescriptor(obj, key as PropertyKey);
      Object.defineProperty(obj, key as PropertyKey, {
        ...desc,
        value: replace,
      });

      map.set(key, { methodMap, origfn, desc });
    },

    /** 清理某对象的缓存或恢复某方法（若未传 method 则恢复全部） */
    clear<T extends object, K extends keyof T>(obj: T, method?: K) {
      const map = cacheMap.get(obj);
      if (!map) return;

      if (method === undefined) {
        for (const [k, entry] of map) {
          Object.defineProperty(obj, k as PropertyKey, {
            ...entry.desc,
            value: entry.origfn,
          });
        }
        cacheMap.delete(obj);
        return;
      }

      const key = method as unknown as string | symbol;
      const entry = map.get(key);
      if (!entry) return;

      // 恢复原方法
      Object.defineProperty(obj, key as PropertyKey, {
        ...entry.desc,
        value: entry.origfn,
      });

      map.delete(key);
      if (map.size === 0) cacheMap.delete(obj);
    },
  };
}

/**
 * 生成安全的缓存键
 * @param args 方法参数
 * @returns 缓存键字符串
 */
function generateCacheKey(args: unknown[]): string {
  try {
    // 使用 JSON.stringify 确保键的唯一性
    return JSON.stringify(args);
  } catch (err) {
    // 如果序列化失败，回退到简单拼接（添加分隔符避免冲突）
    console.warn('参数序列化失败，使用简单拼接策略:', err);
    return args.map((arg, index) => `${index}:${String(arg)}`).join('|');
  }
}

// console.log(`flatten arr is`,flatten(arr2))
console.log(`flatten arr is`, flatten(arr2, Infinity));

console.log(`uniqued arr is`, unique(arr));

console.log(`array of map is`, mapToArr(map));

console.log(`merged map is`, mergeMap(map, map2));

console.log(`the max values' key is`, findMaxValue(map));

console.log(
  `intersection of sets is`,
  intersection(new Set([1, 2, 3]), new Set([3, 4, 5])),
);

console.log(
  `filter map is`,
  filterSet(new Set([2, 4, 6, 5, 7, 11]), (el) => (el as number) % 2 === 0),
);

console.log(`random set el is`, randomElement(new Set([1, 2, 3, 'a', 'b'])));

async function runCacheTests() {
  // const assert = require('assert') as typeof import('assert');
  const cachedObj = createCache();

  // 1) 基本缓存 + 调用计数
  const counterObj = {
    calls: 0,
    add(n: number) {
      this.calls++;
      return n + 5;
    },
  };
  cachedObj.set(counterObj, 'add');
  assert.strictEqual(counterObj.add(5), 10);
  assert.strictEqual(counterObj.add(6), 11);
  assert.strictEqual(counterObj.add(5), 10);
  // 预期只在实际未命中时增加调用
  assert(counterObj.calls <= 3);

  // 2) falsy 值仍应缓存
  const zeroObj = {
    calls: 0,
    sub(a: number, b: number) {
      this.calls++;
      return a - b;
    },
  };
  cachedObj.set(zeroObj, 'sub');
  assert.strictEqual(zeroObj.sub(1, 1), 0);
  assert.strictEqual(zeroObj.sub(1, 1), 0);
  assert.strictEqual(zeroObj.calls, 1);

  // 3) this 绑定保持正确
  const thisObj = {
    value: 10,
    inc(n: number) {
      this.value += n;
      return this.value;
    },
  };
  cachedObj.set(thisObj, 'inc');
  assert.strictEqual(thisObj.value, 10);
  assert.strictEqual(thisObj.inc(1), 11);
  assert.strictEqual(thisObj.inc(1), 11);
  assert.strictEqual(thisObj.value, 11);

  // 4) 异步方法缓存（缓存 Promise 结果）
  const asyncObj = {
    calls: 0,
    async fetch(v: number) {
      this.calls++;
      return Promise.resolve(v * 2);
    },
  };
  cachedObj.set(asyncObj, 'fetch');
  const r1 = await asyncObj.fetch(2);
  const r2 = await asyncObj.fetch(3);
  const r3 = await asyncObj.fetch(2);
  assert.strictEqual(r1, 4);
  assert.strictEqual(r2, 6);
  assert.strictEqual(r3, 4);
  assert.strictEqual(asyncObj.calls, 2);

  // 5) clear 恢复原方法
  cachedObj.clear(counterObj, 'add');
  // 清理后调用应执行原函数并增加 calls
  const prevCalls = counterObj.calls;
  assert.strictEqual(counterObj.add(5), 10);
  assert.strictEqual(counterObj.calls, prevCalls + 1);

  console.log('所有缓存测试通过');
}

runCacheTests();
